﻿using BitMiracle.LibTiff.Classic;
using ELTE.SEM.Data.Connectors;
using ELTE.SEM.Data.Repository;
using ELTE.SEM.Data.ViewModels;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;

namespace ELTE.SEM.Data.Managers
{
    public class ImageManager : ManagerBase
    {
        private SambaConnector connector;
        private ErrorLogRepository ErrorLogRepo { get; set; }
        private ScanImageRepository Repo { get; set; }
        private ScanImagePropertyGroupRepository ImagePropertyGroupRepo { get; set; }
        List<ScanImagePropertyGroup> AvailableImagePropertyGroups { get; set; }

        public ImageManager()
            : base()
        {
            ErrorLogRepo = new ErrorLogRepository(this.Entities);
            Repo = new ScanImageRepository(this.Entities);
            ImagePropertyGroupRepo = new ScanImagePropertyGroupRepository(this.Entities);
            AvailableImagePropertyGroups = ImagePropertyGroupRepo.GetAll().ToList();
        }
        public ImageManager(SemDataEntities entities)
            : base(entities)
        {
            ErrorLogRepo = new ErrorLogRepository(entities);
            Repo = new ScanImageRepository(entities);
            ImagePropertyGroupRepo = new ScanImagePropertyGroupRepository(entities);
            AvailableImagePropertyGroups = ImagePropertyGroupRepo.GetAll().ToList();
        }

        public ImageManager(SemFileServerLoginViewModel model, SemDataEntities entities)
        {
            connector = new SambaConnector(model);
            ErrorLogRepo = new ErrorLogRepository(entities);
            Repo = new ScanImageRepository(entities);
            ImagePropertyGroupRepo = new ScanImagePropertyGroupRepository(entities);
            AvailableImagePropertyGroups = ImagePropertyGroupRepo.GetAll().ToList();
        }


        public List<string> GetFilesInFolder(SemFolderViewModel folderViewModel, string rootPath)
        {
            List<string> filesInFolder = new List<string>();
            try
            {
                filesInFolder = connector.GetFilePathsInFolder(folderViewModel, rootPath, "tiff");
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }
            return filesInFolder;
        }

        public Stream GetFileStreamAsPng(string filePath)
        {
            return connector.GetFileStream(filePath).TiffStreamToPngStream();
        }

        public List<KeyValuePair<string, string>> GetImageData(Stream imageStream)
        {
            List<KeyValuePair<string, string>> ImageData = new List<KeyValuePair<string, string>>();
            try
            {
                using (MemoryStream memStream = new MemoryStream(imageStream.ToByteArray()))
                {
                    using (Tiff image = Tiff.ClientOpen("in-memory", "r", memStream, new TiffStream()))
                    {
                        //image.GetField((TiffTag)
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }
            return ImageData;
        }

        public void AddScanImage(SemDataViewModel model, string rootPath)
        {
            try
            {
                //ellenőrizzük, hogy az adott entitás szerepel-e az adatbázisban
                //minden képre ki kell olvasni az ő adott készítési dátumát
                //kép letöltése
                Func<ScanImage, bool> nameMatch = img => img.FullPath == model.FullName;
                var allImages = Repo.GetAll();
                if (!allImages.Any(nameMatch))
                {

                    SemDataParsedResult parseResult = GetImageProperties(model.FullName, model.Name);
                    //az adatok közül kell a készítés dátuma
                    SemUser SemUser = (SemUser)parseResult.BaseSemData.SingleOrDefault(d => d.Title == "SemUser");
                    //SemUser.Date
                    string[] date = SemUser.Date.Split('/');
                    string[] time = SemUser.Time.Remove(SemUser.Time.IndexOf(" ")).Split(':');
                    if (date.Length == 3 && time.Length == 3)
                    {
                        DateTime created = new DateTime(
                            Convert.ToInt32(date[2]),
                            Convert.ToInt32(date[0]),
                            Convert.ToInt32(date[1]),
                            Convert.ToInt32(time[0]),
                            Convert.ToInt32(time[1]),
                            Convert.ToInt32(time[2]));
                        //az elkészítés dátuma megvan
                        ScanImage image = new ScanImage { Created = created, Name = model.Name, FullPath = model.FullName };
                        //kellenek a hozzá illő tulajdonságok
                        List<ScanImageProperty> imageProperties = GenerateProperties(parseResult);
                        //az biztos hogy minden egyes mérési kép tartalmaz adatokat
                        if (imageProperties.Count > 0
                            && parseResult.UnknownRecords.Count == 0
                            && !parseResult.BaseSemData.Any(pr => pr.UnknownValues.Count > 0)
                            )
                        {
                            image.ScanImageProperties = imageProperties;
                            bool insertResult = Repo.Insert(image);
                            Repo.Save();
                        }
                        else
                        {
                            CheckScanFileProperties(parseResult, image);
                        }
                    }

                }
                else
                {
                    //az adott kép már szerepel az adatbázisban
                    //ellenőrizzük, hogy minden property-je megfelelően hozzá van adva
                    if (allImages.Count(nameMatch) == 1)
                    {
                        ScanImage img = allImages.SingleOrDefault(nameMatch);
                        SemDataParsedResult parseResult = GetImageProperties(model.FullName, model.Name);
                        CheckScanFileProperties(parseResult, img);
                    }
                    else
                    {
                        ErrorLogRepo.AddErrorLog(
                            MethodInfo.GetCurrentMethod().DeclaringType.Name,
                            MethodInfo.GetCurrentMethod().Name,
                            "Név egyezés több mint egy elemre",
                            String.Format("Név: {0}", model.FullName));
                    }
                }
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }

        }

        public void AddScanImage(List<SemDataViewModel> models, string rootPath)
        {
            var allImages = Repo.GetAll().ToList();
            //minden elemre megvizsgáljuk, hogy benne van-e már az adatbázisban.
            //ha benne van, akkor meg kell vizsgálni, hogy a készítés időpontja megegyezik-e
            //a default értékkel. Ha igen, a
            //
            try
            {
                DateTime defaultDate = new DateTime(1985, 10, 7);
                models.ForEach(scan =>
                    {
                        var imageMatch = allImages.SingleOrDefault(image => image.FullPath == scan.FullName);
                        if (imageMatch != null)
                        {
                            //van kép ugyanilyen névvel
                            //ellenőrizzük, hogy a dátum egyezik-e
                            //ha igen, akkor (csak a biztonság kedvéért)
                            //ellenőrizzük, hogy vannak-e tulajdonságai beállítva
                            //if (imageMatch.Created == defaultDate)
                            //{
                            //    if (imageMatch.ScanImageProperties.Count > 0)
                            //    {
                            //        //már vannak tulajdonságok beállítva
                            //    }
                            //    else
                            //    {
                            //        //még nincsenek tulajdonságok beállítva
                            //        //a képet le kell tölteni, és ki kell olvasni a tulajdonságait
                            //    }
                            //}
                            //else
                            //{
 
                            //}
                        }
                        else
                        {
                            ScanImage image = new ScanImage
                            {
                                FullPath = scan.FullName,
                                Name = scan.Name,
                                Created = defaultDate,
                            };
                            Repo.Insert(image);
                            
                        }
                    });
                Repo.Save();
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }
        }

        private Stream GetFileStream(string fullName)
        {
            return connector.GetFileStream(fullName);
        }

        private SemDataParsedResult GetImageProperties(string fullName, string name)
        {
            SemDataParsedResult parseResult = null;
            try
            {
                Stream fileStream = GetFileStream(fullName);
                using (MemoryStream memStream = fileStream.GetMemStream())
                {
                    parseResult = TiffConnector.GetImageData(memStream, name);
                }
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }
            return parseResult;
        }

        private SemDataParsedResult GetImageProperties(Stream fileStream, string name)
        {
            SemDataParsedResult parseResult = null;
            try
            {
                using (MemoryStream memStream = fileStream.GetMemStream())
                {
                    parseResult = TiffConnector.GetImageData(memStream, name);
                }
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }
            return parseResult;
        }

        private void CheckScanFileProperties(SemDataParsedResult result, ScanImage image)
        {
            try
            {

                BackgroundWorker worker = new BackgroundWorker();
                worker.DoWork += new DoWorkEventHandler(delegate(object o, DoWorkEventArgs args)
                {
                    try
                    {
                        //leellenőrizzük, hogy az adott 
                        //property-k között van-e olyan, amelyik nem szerepel
                        //az adatbázisbeli kép entitás propertyjei között
                        foreach (var item in result.BaseSemData)
                        {
                            if (!image.ScanImageProperties.Any(prop => prop.ScanImagePropertyGroup.ImagePropertyGroupName == item.Title))
                            {
                                //ha még nincs ilyen elem, akkor hozzá kell adni
                            }
                        }

                        if (result.UnknownRecords.Count > 0)
                        {
                            //Logoljuk az eredményt:
                            ErrorLogRepo.AddErrorLog(
                                MethodInfo.GetCurrentMethod().DeclaringType.Name,
                                MethodInfo.GetCurrentMethod().Name,
                                String.Format("Az alábbi path-al rendelkező képnél ({0}) ismeretlen rekordok", image.FullPath),
                                String.Join("###", result.UnknownRecords));
                        }

                        Func<BaseSemData, bool> cond = d => d.UnknownValues.Count > 0;
                        if (result.BaseSemData.Any(cond))
                        {
                            result.BaseSemData.Where(cond).ToList().ForEach(data =>
                            {
                                data.UnknownValues.ForEach(p =>
                                    ErrorLogRepo.AddErrorLog(
                                        MethodInfo.GetCurrentMethod().DeclaringType.Name,
                                        MethodInfo.GetCurrentMethod().Name,
                                        String.Format("Ismeretlen property-k az alábbi képnél: {0}", image.FullPath),
                                        String.Format("Rekord név: {0} # Rekord érték {1}", p.Key, p.Value)
                                        )
                                );
                            });
                        }
                    }
                    catch (Exception ex)
                    {
                        ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
                    }

                });
                worker.RunWorkerAsync();
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }

        }

        private List<ScanImageProperty> GenerateProperties(SemDataParsedResult parseResult)
        {
            List<ScanImageProperty> imageProperties = new List<ScanImageProperty>();
            parseResult.BaseSemData.ForEach(Data =>
            {
                //kell a megfelelő típus neve
                ScanImagePropertyGroup group = AvailableImagePropertyGroups.SingleOrDefault(pg => pg.ImagePropertyGroupName == Data.Title);
                if (group != null)
                {
                    //megvan a megfelelő csoport, kell, hogy 
                    //mi a property csoport neve
                    //reflection segitségével végigmegyünk a típus minden elemén
                    foreach (var prop in Data.GetType().GetProperties())
                    {
                        object value = prop.GetValue(Data, null);
                        //
                        if (prop.Name != "Title" && prop.Name != "UnknownValues" && prop.Name != "UnProcessedValues")
                        {
                            imageProperties.Add(
                                new ScanImageProperty
                                {
                                    PropertyGroupId = group.Id,
                                    PropertyName = prop.Name,
                                    PropertyValue = value.ToString()
                                });
                        }
                    }
                }
                else
                {
                    //TODO: log error
                    ErrorLogRepo.AddErrorLog(
                        MethodInfo.GetCurrentMethod().DeclaringType.Name,
                        MethodInfo.GetCurrentMethod().Name,
                        String.Format("Nincs találat az alábbi kép tulajdonság-csoportnévre : {0}", Data.Title),
                        String.Empty);
                }
            });
            return imageProperties;
        }

        /// <summary>
        /// Az adott log bejegyzéshez tartozó képek listázása
        /// </summary>
        /// <param name="logEntryId"></param>
        /// <returns></returns>
        public List<SEMLogEntryPictureViewModel> GetImagesWithData(SEMLogEntryViewModel logEntryModel)
        {
            List<SEMLogEntryPictureViewModel> result = new List<SEMLogEntryPictureViewModel>();
            try
            {
                if (logEntryModel.EntryEnd.HasValue)
                {
                    //var images = Repo.GetAll().Where(img =>
                    //    img.Created >= logEntryModel.EntryBegin
                    //    && img.Created <= logEntryModel.EntryEnd.Value
                    //    );
                    //if (images.Count() > 0)
                    //{

                    //}
                    var images = Repo.GetAll().Take(5);
                    foreach (var image in images)
                    {
                        result.Add(GetImageWithData(image));
                    }
                }
                else
                {
                    //olyan képlista kell amely a bejegyzés eleje és vége között született,
                    //ezért muszáj hogy legyen a bejegyzésnek vége
                    //TODO: log error
                }
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }
            return result;
        }

        public List<SEMLogEntryPictureViewModel> GetImagesByFolder(string folderPath)
        {
            List<SEMLogEntryPictureViewModel> result = new List<SEMLogEntryPictureViewModel>();
            try
            {
                string folderPathWithSlash = String.Concat(folderPath, "/");
                var allImages = Repo.GetAll().Where(f => f.FullPath.Contains(folderPath));
                    allImages.Where(img =>
                        img.FullPath
                        .Replace(img.Name, String.Empty)
                        .Replace("/repdata/Q3D",String.Empty)
                        == folderPathWithSlash
                    )
                    .ToList()
                    .ForEach(scan => result.Add(GetImageWithData(scan)));

            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }
            return result;
        }

        private SEMLogEntryPictureViewModel GetImageWithData(ScanImage image)
        {
            SEMLogEntryPictureViewModel result = null;
            try
            {
                Stream fileStream = GetFileStream(image.FullPath);
                result = new SEMLogEntryPictureViewModel
                {
                    PictureData = GetImageProperties(fileStream, image.Name),
                    PictureStream = fileStream.TiffStreamToPngStream(),
                };
            }
            catch (Exception ex)
            {
                ErrorLogRepo.AddErrorLog(MethodInfo.GetCurrentMethod().DeclaringType.Name, MethodInfo.GetCurrentMethod().Name, ex.ToString(), String.Empty);
            }
            return result;
        }
    }
}
